From 59a819c2d25adcc9bc8523c9ddd9f3947858bc07 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Sat, 19 Oct 2024 22:47:40 +0200 Subject: [PATCH 1/8] Check if colums exsist... --- lib/Migration/Version1Date20241019205009.php | 41 ++++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/Migration/Version1Date20241019205009.php b/lib/Migration/Version1Date20241019205009.php index c055e05291..857091a310 100755 --- a/lib/Migration/Version1Date20241019205009.php +++ b/lib/Migration/Version1Date20241019205009.php @@ -15,9 +15,6 @@ use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; -/** - * FIXME Auto-generated migration step: Please modify to your needs! - */ class Version1Date20241019205009 extends SimpleMigrationStep { /** @@ -38,26 +35,38 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - // Update the abilities table to add the base column + // Update the openregister_sources table $table = $schema->getTable('openregister_sources'); - $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); - $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); - $table->addIndex(['uuid'], 'openregister_sources_uuid_index'); + if (!$table->hasColumn('uuid')) { + $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); + $table->addIndex(['uuid'], 'openregister_sources_uuid_index'); + } + if (!$table->hasColumn('version')) { + $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); + } - // Update the abilities table to add the base column + // Update the openregister_schemas table $table = $schema->getTable('openregister_schemas'); - $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); - $table->addIndex(['uuid'], 'openregister_sources_uuid_index'); + if (!$table->hasColumn('uuid')) { + $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); + $table->addIndex(['uuid'], 'openregister_schemas_uuid_index'); + } - // Update the abilities table to add the base column + // Update the openregister_registers table $table = $schema->getTable('openregister_registers'); - $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); - $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); - $table->addIndex(['uuid'], 'openregister_sources_uuid_index'); + if (!$table->hasColumn('uuid')) { + $table->addColumn(name: 'uuid', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255]); + $table->addIndex(['uuid'], 'openregister_registers_uuid_index'); + } + if (!$table->hasColumn('version')) { + $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); + } - // Update the abilities table to add the base column + // Update the openregister_objects table $table = $schema->getTable('openregister_objects'); - $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); + if (!$table->hasColumn('version')) { + $table->addColumn(name: 'version', typeName: Types::STRING, options: ['notnull' => true, 'length' => 255, 'default' => '0.0.1']); + } return $schema; } From e6e089c9854b86b8259435b1604c13a968aba3d7 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 19 Oct 2024 20:48:46 +0000 Subject: [PATCH 2/8] Bump version to 0.1.15 --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index ffc773a526..49b126ffcc 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenRegister/.github/issues/new/choose) Create a [feature request](https://github.com/OpenRegister/.github/issues/new/choose) ]]> - 0.1.14 + 0.1.15 agpl Conduction OpenRegister From 8189fb041da50c9dd5d8dda9b7170f4dde68ca0a Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Sun, 20 Oct 2024 21:07:39 +0200 Subject: [PATCH 3/8] Fixing the deletion of objects --- lib/Controller/ObjectsController.php | 11 ++++++++++- lib/Db/ObjectEntity.php | 26 ++++++++++++++++---------- lib/Db/ObjectEntityMapper.php | 13 ++++++++----- src/views/object/ObjectDetails.vue | 4 ++-- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/Controller/ObjectsController.php b/lib/Controller/ObjectsController.php index e1aa1f28d5..e852fa49d9 100644 --- a/lib/Controller/ObjectsController.php +++ b/lib/Controller/ObjectsController.php @@ -64,12 +64,21 @@ public function index(ObjectService $objectService, SearchService $searchService { $filters = $this->request->getParams(); $fieldsToSearch = ['uuid', 'register', 'schema']; + $extend = ['schema', 'register']; $searchParams = $searchService->createMySQLSearchParams(filters: $filters); $searchConditions = $searchService->createMySQLSearchConditions(filters: $filters, fieldsToSearch: $fieldsToSearch); $filters = $searchService->unsetSpecialQueryParams(filters: $filters); - return new JSONResponse(['results' => $this->objectEntityMapper->findAll(filters: $filters, searchConditions: $searchConditions, searchParams: $searchParams)]); + // @todo: figure out how to use extend here + $results = $this->objectEntityMapper->findAll(filters: $filters); + + // We dont want to return the entity, but the object (and kant reley on the normal serilzier) + foreach ($results as $key => $result) { + $results[$key] = $result->getObject(); + } + + return new JSONResponse(['results' => $results]); } /** diff --git a/lib/Db/ObjectEntity.php b/lib/Db/ObjectEntity.php index ab941cc7c2..9bbfbb76e3 100644 --- a/lib/Db/ObjectEntity.php +++ b/lib/Db/ObjectEntity.php @@ -62,17 +62,23 @@ public function hydrate(array $object): self public function jsonSerialize(): array { -// return [ -// 'id' => $this->id, -// 'uuid' => $this->uuid, -// 'version' => $this->version, -// 'register' => $this->register, -// 'schema' => $this->schema, -// 'object' => $this->object, -// 'updated' => isset($this->updated) ? $this->updated->format('c') : null, -// 'created' => isset($this->created) ? $this->created->format('c') : null -// ]; +// return $this->object; } + + public function getObject(): array + { + return [ + 'id' => $this->id, + 'uuid' => $this->uuid, + 'version' => $this->version, + 'register' => $this->register, + 'schema' => $this->schema, + 'object' => $this->object, + 'updated' => isset($this->updated) ? $this->updated->format('c') : null, + 'created' => isset($this->created) ? $this->created->format('c') : null + ]; + } + } diff --git a/lib/Db/ObjectEntityMapper.php b/lib/Db/ObjectEntityMapper.php index 4a32555eea..dbe9c6a014 100644 --- a/lib/Db/ObjectEntityMapper.php +++ b/lib/Db/ObjectEntityMapper.php @@ -30,22 +30,25 @@ public function __construct(IDBConnection $db, MySQLJsonService $mySQLJsonServic } /** - * Find an object by ID + * Find an object by ID or UUID * - * @param int $id The ID of the object to find + * @param int|string $idOrUuid The ID or UUID of the object to find * @return ObjectEntity The ObjectEntity */ - public function find(int $id): ObjectEntity + public function find($idOrUuid): ObjectEntity { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('openregister_objects') ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) + $qb->expr()->orX( + $qb->expr()->eq('id', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_INT)), + $qb->expr()->eq('uuid', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_STR)) + ) ); - return $this->findEntity(query: $qb); + return $this->findEntity($qb); } /** diff --git a/src/views/object/ObjectDetails.vue b/src/views/object/ObjectDetails.vue index d767f2118e..a33417f8a3 100644 --- a/src/views/object/ObjectDetails.vue +++ b/src/views/object/ObjectDetails.vue @@ -52,9 +52,9 @@ import { objectStore, navigationStore } from '../../store/store.js'
-

+

 								{{ JSON.stringify(objectStore.objectItem.object, null, 2) }}
-							

+
From bceee6df6b031f1432645a79cde942a0b7fb9461 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 21 Oct 2024 00:52:09 +0200 Subject: [PATCH 4/8] Added loging logic --- lib/Controller/ObjectsController.php | 2 +- lib/Db/Log.php | 71 ++++++++++++++++++ lib/Db/LogMapper.php | 75 ++++++++++++++++++++ lib/Db/ObjectEntity.php | 7 +- lib/Migration/Version1Date20241020231700.php | 72 +++++++++++++++++++ lib/Service/ObjectService.php | 63 +++++++++++++--- 6 files changed, 276 insertions(+), 14 deletions(-) create mode 100644 lib/Db/Log.php create mode 100644 lib/Db/LogMapper.php create mode 100755 lib/Migration/Version1Date20241020231700.php diff --git a/lib/Controller/ObjectsController.php b/lib/Controller/ObjectsController.php index e852fa49d9..faf3ecfbe7 100644 --- a/lib/Controller/ObjectsController.php +++ b/lib/Controller/ObjectsController.php @@ -75,7 +75,7 @@ public function index(ObjectService $objectService, SearchService $searchService // We dont want to return the entity, but the object (and kant reley on the normal serilzier) foreach ($results as $key => $result) { - $results[$key] = $result->getObject(); + $results[$key] = $result->getObjectArray(); } return new JSONResponse(['results' => $results]); diff --git a/lib/Db/Log.php b/lib/Db/Log.php new file mode 100644 index 0000000000..6c371616fd --- /dev/null +++ b/lib/Db/Log.php @@ -0,0 +1,71 @@ +addType(fieldName: 'uuid', type: 'string'); + $this->addType(fieldName: 'object', type: 'integer'); + $this->addType(fieldName: 'changed', type: 'json'); + $this->addType(fieldName: 'user', type: 'string'); + $this->addType(fieldName: 'userName', type: 'string'); + $this->addType(fieldName: 'session', type: 'string'); + $this->addType(fieldName: 'created', type: 'datetime'); + } + + public function getJsonFields(): array + { + return array_keys( + array_filter($this->getFieldTypes(), function ($field) { + return $field === 'json'; + }) + ); + } + + public function hydrate(array $object): self + { + $jsonFields = $this->getJsonFields(); + + foreach ($object as $key => $value) { + if (in_array($key, $jsonFields) === true && $value === []) { + $value = null; + } + + $method = 'set'.ucfirst($key); + + try { + $this->$method($value); + } catch (\Exception $exception) { + } + } + + return $this; + } + + public function jsonSerialize(): array + { + return [ + 'id' => $this->id, + 'uuid' => $this->uuid, + 'object' => $this->object, + 'changed' => $this->changed, + 'user' => $this->user, + 'userName' => $this->userName, + 'session' => $this->session, + 'created' => isset($this->created) ? $this->created->format('c') : null + ]; + } +} diff --git a/lib/Db/LogMapper.php b/lib/Db/LogMapper.php new file mode 100644 index 0000000000..4048d89e51 --- /dev/null +++ b/lib/Db/LogMapper.php @@ -0,0 +1,75 @@ +db->getQueryBuilder(); + + $qb->select('*') + ->from('openregister_logs') + ->where( + $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) + ); + + return $this->findEntity(query: $qb); + } + + public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array + { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from('openregister_logs') + ->setMaxResults($limit) + ->setFirstResult($offset); + + foreach ($filters as $filter => $value) { + if ($value === 'IS NOT NULL') { + $qb->andWhere($qb->expr()->isNotNull($filter)); + } elseif ($value === 'IS NULL') { + $qb->andWhere($qb->expr()->isNull($filter)); + } else { + $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); + } + } + + if (!empty($searchConditions)) { + $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); + foreach ($searchParams as $param => $value) { + $qb->setParameter($param, $value); + } + } + + return $this->findEntities(query: $qb); + } + + public function createFromArray(array $object): Log + { + $log = new Log(); + $log->hydrate(object: $object); + + // Set uuid if not provided + if ($log->getUuid() === null) { + $log->setUuid(Uuid::v4()); + } + + return $this->insert(entity: $log); + } + + // We dont need update as we dont change the log +} diff --git a/lib/Db/ObjectEntity.php b/lib/Db/ObjectEntity.php index 9bbfbb76e3..969aa8d14f 100644 --- a/lib/Db/ObjectEntity.php +++ b/lib/Db/ObjectEntity.php @@ -13,6 +13,7 @@ class ObjectEntity extends Entity implements JsonSerializable protected ?string $schema = null; protected ?string $version = null; protected ?array $object = []; + protected ?string $textRepresentation = null; protected ?DateTime $updated = null; protected ?DateTime $created = null; @@ -22,6 +23,7 @@ public function __construct() { $this->addType(fieldName:'schema', type: 'string'); $this->addType(fieldName: 'version', type: 'string'); $this->addType(fieldName:'object', type: 'json'); + $this->addType(fieldName:'textRepresentation', type: 'text'); $this->addType(fieldName:'updated', type: 'datetime'); $this->addType(fieldName:'created', type: 'datetime'); } @@ -62,12 +64,10 @@ public function hydrate(array $object): self public function jsonSerialize(): array { -// - return $this->object; } - public function getObject(): array + public function getObjectArray(): array { return [ 'id' => $this->id, @@ -76,6 +76,7 @@ public function getObject(): array 'register' => $this->register, 'schema' => $this->schema, 'object' => $this->object, + 'textRepresentation' => $this->textRepresentation, 'updated' => isset($this->updated) ? $this->updated->format('c') : null, 'created' => isset($this->created) ? $this->created->format('c') : null ]; diff --git a/lib/Migration/Version1Date20241020231700.php b/lib/Migration/Version1Date20241020231700.php new file mode 100755 index 0000000000..95d816f4bd --- /dev/null +++ b/lib/Migration/Version1Date20241020231700.php @@ -0,0 +1,72 @@ +hasTable('openregister_logs')) { + $table = $schema->createTable('openregister_logs'); + $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'notnull' => true]); + $table->addColumn('uuid', Types::STRING, ['notnull' => false, 'length' => 255]); + $table->addColumn('object', Types::INTEGER, ['notnull' => true]); + $table->addColumn('changed', Types::JSON, ['notnull' => true]); + $table->addColumn('user', Types::STRING, ['notnull' => true, 'length' => 255]); + $table->addColumn('user_name', Types::STRING, ['notnull' => true, 'length' => 255]); + $table->addColumn('session', Types::STRING, ['notnull' => true, 'length' => 255]); + $table->addColumn('request', Types::STRING, ['notnull' => false, 'length' => 255]); + $table->addColumn('created', Types::DATETIME, ['notnull' => true]); + + $table->setPrimaryKey(['id']); + $table->addIndex(['user'], 'openregister_logs_user_index'); + $table->addIndex(['uuid'], 'openregister_logs_uuid_index'); + } + + ///Update the openregister_objects table + $table = $schema->getTable('openregister_objects'); + if (!$table->hasColumn('text_representation')) { + $table->addColumn(name: 'text_representation', typeName: Types::TEXT, options: ['notnull' => false]); + } + + return $schema; + } + + /** + * @param IOutput $output + * @param Closure(): ISchemaWrapper $schemaClosure + * @param array $options + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { + } +} diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index 6adb8a3bda..b103c1ae89 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -10,6 +10,8 @@ use OCA\OpenRegister\Db\RegisterMapper; use OCA\OpenRegister\Db\ObjectEntity; use OCA\OpenRegister\Db\ObjectEntityMapper; +use OCA\OpenRegister\Db\Log; +use OCA\OpenRegister\Db\LogMapper; use Symfony\Component\Uid\Uuid; class ObjectService @@ -25,11 +27,12 @@ class ObjectService * * @param ObjectEntityMapper $objectEntityMapper The ObjectEntity Mapper */ - public function __construct(ObjectEntityMapper $objectEntityMapper, RegisterMapper $registerMapper, SchemaMapper $schemaMapper) + public function __construct(ObjectEntityMapper $objectEntityMapper, RegisterMapper $registerMapper, SchemaMapper $schemaMapper, LogMapper $logMapper) { $this->objectEntityMapper = $objectEntityMapper; $this->registerMapper = $registerMapper; $this->schemaMapper = $schemaMapper; + $this->logMapper = $logMapper; } public function find(int|string $id) { @@ -50,6 +53,7 @@ public function createFromArray(array $object) { public function updateFromArray(string $id, array $object, bool $updatedObject) { $object['id'] = $id; + return $this->saveObject( register: $this->getRegister(), schema: $this->getSchema(), @@ -165,7 +169,6 @@ public function getObjects(?string $objectType = null, ?int $register = null, ?i */ public function saveObject(int $register, int $schema, array $object): ObjectEntity { - // Convert register and schema to their respective objects if they are strings if (is_string($register)) { $register = $this->registerMapper->find($register); @@ -185,10 +188,8 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt $objectEntity->setSchema($schema); ///return $this->objectEntityMapper->update($objectEntity); } - - // Does the object have an if? - if (isset($object['id'])) { + if (isset($object['id']) && !empty($object['id'])) { // Update existing object $objectEntity->setUuid($object['id']); } else { @@ -197,17 +198,59 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt $object['id'] = $objectEntity->getUuid(); } + $oldObject = $objectEntity->getObject(); $objectEntity->setObject($object); + $changed = []; + + foreach ($object as $key => $value) { + if (!isset($oldObject[$key]) || $oldObject[$key] !== $value) { + $changed[$key] = [ + 'old' => $oldObject[$key] ?? null, + 'new' => $value + ]; + } + } + + // Check for removed properties + foreach ($oldObject as $key => $value) { + if (!isset($object[$key])) { + $changed[$key] = [ + 'old' => $value, + 'new' => null + ]; + } + } + // Normal loging + //$changed = $objectEntity->getUpdatedFields(); + + + // If the object has no uuid, create a new one + if (empty($objectEntity->getUuid())) { + $objectEntity->setUuid(Uuid::v4()); + } + if($objectEntity->getId()){ - return $this->objectEntityMapper->update($objectEntity); + $objectEntity = $this->objectEntityMapper->update($objectEntity); + } + else { + $objectEntity = $this->objectEntityMapper->insert($objectEntity); } - return $this->objectEntityMapper->insert($objectEntity); - //@todo mongodb support + // Create a log entry + $user = \OC::$server->getUserSession()->getUser(); - // Handle external source here if needed - throw new \Exception('Unsupported source type'); + $log = new Log(); + $log->setUuid(Uuid::v4()); + $log->setObject($objectEntity->getId()); + $log->setChanged($changed); + $log->setUser($user->getUID()); + $log->setUserName($user->getDisplayName()); + $log->setSession(session_id()); + $log->setCreated(new \DateTime()); + $this->logMapper->insert($log); + + return $objectEntity; } From d07ab41387e20cf4c40842f6bf708ecb5a4430cf Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 21 Oct 2024 07:48:14 +0200 Subject: [PATCH 5/8] Finished the BL of audit trails --- appinfo/routes.php | 2 + lib/Controller/ObjectsController.php | 20 +++++- lib/Db/{Log.php => AuditTrail.php} | 11 +++- .../{LogMapper.php => AuditTrailMapper.php} | 8 +-- lib/Migration/Version1Date20241020231700.php | 6 +- lib/Service/ObjectService.php | 24 +++++--- src/store/modules/object.js | 10 +++ src/views/object/ObjectDetails.vue | 61 ++++++++++++++++++- 8 files changed, 125 insertions(+), 17 deletions(-) rename lib/Db/{Log.php => AuditTrail.php} (79%) rename lib/Db/{LogMapper.php => AuditTrailMapper.php} (90%) diff --git a/appinfo/routes.php b/appinfo/routes.php index 93948ea16d..dde840430a 100755 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,5 +10,7 @@ 'routes' => [ ['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'], ['name' => 'registers#objects', 'url' => '/api/registers-objects/{register}/{schema}', 'verb' => 'GET'], + ['name' => 'objects#auditTrails', 'url' => '/api/audit-trails/{id}', 'verb' => 'GET'], + ], ]; diff --git a/lib/Controller/ObjectsController.php b/lib/Controller/ObjectsController.php index faf3ecfbe7..d375eb0b13 100644 --- a/lib/Controller/ObjectsController.php +++ b/lib/Controller/ObjectsController.php @@ -6,6 +6,7 @@ use OCA\OpenRegister\Service\SearchService; use OCA\OpenRegister\Db\ObjectEntity; use OCA\OpenRegister\Db\ObjectEntityMapper; +use OCA\OpenRegister\Db\AuditTrailMapper; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\JSONResponse; @@ -25,7 +26,8 @@ public function __construct( $appName, IRequest $request, private readonly IAppConfig $config, - private readonly ObjectEntityMapper $objectEntityMapper + private readonly ObjectEntityMapper $objectEntityMapper, + private readonly AuditTrailMapper $auditTrailMapper ) { parent::__construct($appName, $request); @@ -171,4 +173,20 @@ public function destroy(int $id): JSONResponse return new JSONResponse([]); } + + /** + * Retrieves a list of logs for an object + * + * This method returns a JSON response containing the logs for a specific object. + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $id The ID of the object to delete + * @return JSONResponse An empty JSON response + */ + public function auditTrails(int $id): JSONResponse + { + return new JSONResponse($this->auditTrailMapper->findAll(filters: ['object' => $id])); + } } diff --git a/lib/Db/Log.php b/lib/Db/AuditTrail.php similarity index 79% rename from lib/Db/Log.php rename to lib/Db/AuditTrail.php index 6c371616fd..b3399958aa 100644 --- a/lib/Db/Log.php +++ b/lib/Db/AuditTrail.php @@ -6,23 +6,29 @@ use JsonSerializable; use OCP\AppFramework\Db\Entity; -class Log extends Entity implements JsonSerializable +class AuditTrail extends Entity implements JsonSerializable { protected ?string $uuid = null; protected ?int $object = null; + protected ?string $action = null; protected ?array $changed = null; protected ?string $user = null; protected ?string $userName = null; protected ?string $session = null; + protected ?string $request = null; + protected ?string $ipAddress = null; protected ?DateTime $created = null; public function __construct() { $this->addType(fieldName: 'uuid', type: 'string'); $this->addType(fieldName: 'object', type: 'integer'); + $this->addType(fieldName: 'action', type: 'string'); $this->addType(fieldName: 'changed', type: 'json'); $this->addType(fieldName: 'user', type: 'string'); $this->addType(fieldName: 'userName', type: 'string'); $this->addType(fieldName: 'session', type: 'string'); + $this->addType(fieldName: 'request', type: 'string'); + $this->addType(fieldName: 'ipAddress', type: 'string'); $this->addType(fieldName: 'created', type: 'datetime'); } @@ -61,10 +67,13 @@ public function jsonSerialize(): array 'id' => $this->id, 'uuid' => $this->uuid, 'object' => $this->object, + 'action' => $this->action, 'changed' => $this->changed, 'user' => $this->user, 'userName' => $this->userName, 'session' => $this->session, + 'request' => $this->request, + 'ipAddress' => $this->ipAddress, 'created' => isset($this->created) ? $this->created->format('c') : null ]; } diff --git a/lib/Db/LogMapper.php b/lib/Db/AuditTrailMapper.php similarity index 90% rename from lib/Db/LogMapper.php rename to lib/Db/AuditTrailMapper.php index 4048d89e51..3f10f99e82 100644 --- a/lib/Db/LogMapper.php +++ b/lib/Db/AuditTrailMapper.php @@ -9,11 +9,11 @@ use OCP\IDBConnection; use Symfony\Component\Uid\Uuid; -class LogMapper extends QBMapper +class AuditTrailMapper extends QBMapper { public function __construct(IDBConnection $db) { - parent::__construct($db, 'openregister_logs'); + parent::__construct($db, 'openregister_audit_trails'); } public function find(int $id): Log @@ -21,7 +21,7 @@ public function find(int $id): Log $qb = $this->db->getQueryBuilder(); $qb->select('*') - ->from('openregister_logs') + ->from('openregister_audit_trails') ->where( $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); @@ -34,7 +34,7 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters $qb = $this->db->getQueryBuilder(); $qb->select('*') - ->from('openregister_logs') + ->from('openregister_audit_trails') ->setMaxResults($limit) ->setFirstResult($offset); diff --git a/lib/Migration/Version1Date20241020231700.php b/lib/Migration/Version1Date20241020231700.php index 95d816f4bd..a16c799bd1 100755 --- a/lib/Migration/Version1Date20241020231700.php +++ b/lib/Migration/Version1Date20241020231700.php @@ -36,16 +36,18 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $schema = $schemaClosure(); // create the openregister_logs table - if (!$schema->hasTable('openregister_logs')) { - $table = $schema->createTable('openregister_logs'); + if (!$schema->hasTable('openregister_audit_trails')) { + $table = $schema->createTable('openregister_audit_trails'); $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'notnull' => true]); $table->addColumn('uuid', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('object', Types::INTEGER, ['notnull' => true]); + $table->addColumn('action', Types::STRING, ['notnull' => true, 'default' => 'update']); $table->addColumn('changed', Types::JSON, ['notnull' => true]); $table->addColumn('user', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('user_name', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('session', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('request', Types::STRING, ['notnull' => false, 'length' => 255]); + $table->addColumn('ip_address', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('created', Types::DATETIME, ['notnull' => true]); $table->setPrimaryKey(['id']); diff --git a/lib/Service/ObjectService.php b/lib/Service/ObjectService.php index b103c1ae89..11f021ca83 100755 --- a/lib/Service/ObjectService.php +++ b/lib/Service/ObjectService.php @@ -10,8 +10,8 @@ use OCA\OpenRegister\Db\RegisterMapper; use OCA\OpenRegister\Db\ObjectEntity; use OCA\OpenRegister\Db\ObjectEntityMapper; -use OCA\OpenRegister\Db\Log; -use OCA\OpenRegister\Db\LogMapper; +use OCA\OpenRegister\Db\AuditTrail; +use OCA\OpenRegister\Db\AuditTrailMapper; use Symfony\Component\Uid\Uuid; class ObjectService @@ -20,19 +20,24 @@ class ObjectService private int $register; private int $schema; - private $callLogMapper; + private AuditTrailMapper $auditTrailMapper; /** * The constructor sets al needed variables. * * @param ObjectEntityMapper $objectEntityMapper The ObjectEntity Mapper */ - public function __construct(ObjectEntityMapper $objectEntityMapper, RegisterMapper $registerMapper, SchemaMapper $schemaMapper, LogMapper $logMapper) + public function __construct( + ObjectEntityMapper $objectEntityMapper, + RegisterMapper $registerMapper, + SchemaMapper $schemaMapper, + AuditTrailMapper $auditTrailMapper + ) { $this->objectEntityMapper = $objectEntityMapper; $this->registerMapper = $registerMapper; $this->schemaMapper = $schemaMapper; - $this->logMapper = $logMapper; + $this->auditTrailMapper = $auditTrailMapper; } public function find(int|string $id) { @@ -232,23 +237,28 @@ public function saveObject(int $register, int $schema, array $object): ObjectEnt if($objectEntity->getId()){ $objectEntity = $this->objectEntityMapper->update($objectEntity); + $action = 'update'; } else { $objectEntity = $this->objectEntityMapper->insert($objectEntity); + $action = 'create'; } // Create a log entry $user = \OC::$server->getUserSession()->getUser(); - $log = new Log(); + $log = new AuditTrail(); $log->setUuid(Uuid::v4()); $log->setObject($objectEntity->getId()); + $log->setAction($action); $log->setChanged($changed); $log->setUser($user->getUID()); $log->setUserName($user->getDisplayName()); $log->setSession(session_id()); + $log->setRequest(\OC::$server->getRequest()->getId()); + $log->setIpAddress(\OC::$server->getRequest()->getRemoteAddress()); $log->setCreated(new \DateTime()); - $this->logMapper->insert($log); + $this->auditTrailMapper->insert($log); return $objectEntity; } diff --git a/src/store/modules/object.js b/src/store/modules/object.js index 97c4d730bf..8ef2d2936b 100644 --- a/src/store/modules/object.js +++ b/src/store/modules/object.js @@ -6,6 +6,8 @@ export const useObjectStore = defineStore('object', { state: () => ({ objectItem: false, objectList: [], + auditTrailItem: false, + auditTrails: [], }), actions: { setObjectItem(objectItem) { @@ -18,6 +20,14 @@ export const useObjectStore = defineStore('object', { ) console.log('Object list set to ' + objectList.length + ' items') }, + setAuditTrailItem(auditTrailItem) { + this.auditTrailItem = auditTrailItem && new AuditTrail(auditTrailItem) + }, + setAuditTrails(auditTrails) { + this.auditTrails = auditTrails.map( + (auditTrail) => new AuditTrail(auditTrail), + ) + }, /* istanbul ignore next */ // ignore this for Jest until moved into a service async refreshObjectList(search = null) { // @todo this might belong in a service? diff --git a/src/views/object/ObjectDetails.vue b/src/views/object/ObjectDetails.vue index a33417f8a3..463c7212f5 100644 --- a/src/views/object/ObjectDetails.vue +++ b/src/views/object/ObjectDetails.vue @@ -78,7 +78,28 @@ import { objectStore, navigationStore } from '../../store/store.js'
- No logs found + + + + + + + + + + + + + + +
TijdstipGebruikerActieDetails
{{ new Date(auditTrail.created).toLocaleString() }}{{ auditTrail.userName }}{{ auditTrail.action }} + + + Bekijk details + +
@@ -94,7 +115,7 @@ import { BTabs, BTab } from 'bootstrap-vue' import DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue' import Pencil from 'vue-material-design-icons/Pencil.vue' import TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue' - +import TimelineQuestionOutline from 'vue-material-design-icons/TimelineQuestionOutline.vue' export default { name: 'ObjectDetails', components: { @@ -106,7 +127,43 @@ export default { DotsHorizontal, Pencil, TrashCanOutline, + TimelineQuestionOutline, + }, + data() { + return { + auditTrailLoading: false, + auditTrails: [], + } + }, + mounted() { + this.getAuditTrails(); }, + methods: { + getAuditTrails() { + this.syncLoading = true + fetch( + `/index.php/apps/openregister/api/audit-trails/${objectStore.objectItem.id}`, + { + method: 'GET', + }, + ) + .then( + (response) => { + response.json().then( + (data) => { + this.auditTrails = data + console.log(this.auditTrails) + this.auditTrailLoading = false + }, + ) + }, + ) + .catch((err) => { + this.error = err + this.auditTrailLoading = false + }) + }, + } } From 933422f6af2ec0787d7206e33cd8ceeec928da01 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 21 Oct 2024 05:49:25 +0000 Subject: [PATCH 6/8] Bump version to 0.1.16 --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 49b126ffcc..6e8b68e26f 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenRegister/.github/issues/new/choose) Create a [feature request](https://github.com/OpenRegister/.github/issues/new/choose) ]]> - 0.1.15 + 0.1.16 agpl Conduction OpenRegister From f8f211ab2712a097b7e19ac069518f0626820ba5 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 21 Oct 2024 08:59:13 +0200 Subject: [PATCH 7/8] Make the loging travebel --- lib/Db/AuditTrail.php | 9 +++++++++ lib/Migration/Version1Date20241020231700.php | 3 +++ 2 files changed, 12 insertions(+) diff --git a/lib/Db/AuditTrail.php b/lib/Db/AuditTrail.php index b3399958aa..fa06c25181 100644 --- a/lib/Db/AuditTrail.php +++ b/lib/Db/AuditTrail.php @@ -9,6 +9,8 @@ class AuditTrail extends Entity implements JsonSerializable { protected ?string $uuid = null; + protected ?int $schema = null; + protected ?int $register = null; protected ?int $object = null; protected ?string $action = null; protected ?array $changed = null; @@ -17,10 +19,13 @@ class AuditTrail extends Entity implements JsonSerializable protected ?string $session = null; protected ?string $request = null; protected ?string $ipAddress = null; + protected ?string $version = null; protected ?DateTime $created = null; public function __construct() { $this->addType(fieldName: 'uuid', type: 'string'); + $this->addType(fieldName: 'schema', type: 'integer'); + $this->addType(fieldName: 'register', type: 'integer'); $this->addType(fieldName: 'object', type: 'integer'); $this->addType(fieldName: 'action', type: 'string'); $this->addType(fieldName: 'changed', type: 'json'); @@ -29,6 +34,7 @@ public function __construct() { $this->addType(fieldName: 'session', type: 'string'); $this->addType(fieldName: 'request', type: 'string'); $this->addType(fieldName: 'ipAddress', type: 'string'); + $this->addType(fieldName: 'version', type: 'string'); $this->addType(fieldName: 'created', type: 'datetime'); } @@ -66,6 +72,8 @@ public function jsonSerialize(): array return [ 'id' => $this->id, 'uuid' => $this->uuid, + 'schema' => $this->schema, + 'register' => $this->register, 'object' => $this->object, 'action' => $this->action, 'changed' => $this->changed, @@ -74,6 +82,7 @@ public function jsonSerialize(): array 'session' => $this->session, 'request' => $this->request, 'ipAddress' => $this->ipAddress, + 'version' => $this->version, 'created' => isset($this->created) ? $this->created->format('c') : null ]; } diff --git a/lib/Migration/Version1Date20241020231700.php b/lib/Migration/Version1Date20241020231700.php index a16c799bd1..2ef8e1704c 100755 --- a/lib/Migration/Version1Date20241020231700.php +++ b/lib/Migration/Version1Date20241020231700.php @@ -40,6 +40,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table = $schema->createTable('openregister_audit_trails'); $table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'notnull' => true]); $table->addColumn('uuid', Types::STRING, ['notnull' => false, 'length' => 255]); + $table->addColumn('schema', Types::INTEGER, ['notnull' => false]); + $table->addColumn('regsiter', Types::INTEGER, ['notnull' => false]); $table->addColumn('object', Types::INTEGER, ['notnull' => true]); $table->addColumn('action', Types::STRING, ['notnull' => true, 'default' => 'update']); $table->addColumn('changed', Types::JSON, ['notnull' => true]); @@ -48,6 +50,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addColumn('session', Types::STRING, ['notnull' => true, 'length' => 255]); $table->addColumn('request', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('ip_address', Types::STRING, ['notnull' => false, 'length' => 255]); + $table->addColumn('version', Types::STRING, ['notnull' => false, 'length' => 255]); $table->addColumn('created', Types::DATETIME, ['notnull' => true]); $table->setPrimaryKey(['id']); From 256bd682c70e233e71afe4f095fe4a5bc50d7954 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 21 Oct 2024 06:59:55 +0000 Subject: [PATCH 8/8] Bump version to 0.1.17 --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 6e8b68e26f..9c1ae67769 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -22,7 +22,7 @@ Create a [bug report](https://github.com/OpenRegister/.github/issues/new/choose) Create a [feature request](https://github.com/OpenRegister/.github/issues/new/choose) ]]> - 0.1.16 + 0.1.17 agpl Conduction OpenRegister